/*
  Adrenaline
  Copyright (C) 2016-2018, TheFloW

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <psp2/appmgr.h>
#include <psp2/io/dirent.h>
#include <psp2/io/fcntl.h>
#include <psp2/io/stat.h>
#include <psp2/kernel/clib.h>
#include <psp2/kernel/modulemgr.h>
#include <psp2/kernel/sysmem.h>
#include <psp2/kernel/processmgr.h>

#include <stdio.h>
#include <string.h>

#include <taihen.h>

#include "utils.h"
// #include "../adrenaline_compat.h"

char *sceLsdbGetName(void *a1);
// char *sceLsdbGetOriginalPath(void *a1);
// char *sceLsdbGetSelfPath(void *a1);

static tai_hook_ref_t sceSysmoduleLoadModuleInternalWithArgRef;
static tai_hook_ref_t sceSysmoduleUnloadModuleInternalWithArgRef;
static tai_hook_ref_t scePafMiscLoadModuleRef;
static tai_hook_ref_t sceLsdbGetTypeRef;
static tai_hook_ref_t sceLsdbGetSelfPathRef;

static SceUID hooks[5];

static int isAdrenalineApp(const char *titleid, char *path)
{
  if (sceClibStrncmp(titleid, "NPXS10007", 9) == 0)
    sceClibSnprintf(path, 1024, "pd0:app/%s/sce_module/adrenaline_user.suprx", titleid);
  else if (sceClibStrncmp(titleid, "NPXS", 4) == 0)
    sceClibSnprintf(path, 1024, "vs0:app/%s/sce_module/adrenaline_user.suprx", titleid);
  else
    sceClibSnprintf(path, 1024, "ux0:app/%s/sce_module/adrenaline_user.suprx", titleid);

  return checkFileExist(path);
}

static int sceLsdbGetTypePatched(void *a1) {
  int type = TAI_CONTINUE(int, sceLsdbGetTypeRef, a1);
  SceUID pid;
  char titleid[12];
  char *name = sceLsdbGetName(a1);

  // sceClibPrintf("sceLsdbGetTypePatched: type: %d, name: %s\n", type, name);

  if (name && sceAppMgrGetIdByName(&pid, name) >= 0) {
    sceAppMgrAppParamGetString(pid, 12, titleid, sizeof(titleid));
    // sceClibPrintf("sceLsdbGetTypePatched: type: %d, pid: 0x%X, name: %s, titleid: %s\n", type, pid, name, titleid);
    if (sceClibStrncmp(titleid, "NPXS10028", 9) == 0)
      return 3;
  }

  return type;
}

static char *sceLsdbGetSelfPathPatched(void *a1) {
  char *selfpath = TAI_CONTINUE(char *, sceLsdbGetSelfPathRef, a1);
  char *name = sceLsdbGetName(a1);

  // sceClibPrintf("sceLsdbGetSelfPathPatched: name: %s, selfpath: %s\n", name, selfpath);

  if (name && selfpath) {
    char prx_path[1024];
    if (isAdrenalineApp(name, prx_path)) {
      return "vs0:app/NPXS10028/eboot.bin";
      // sceClibSnprintf(selfpath, 1024, "vs0:app/NPXS10028/eboot.bin");
    }
  }

  return selfpath;
}

static int scePafMiscLoadModulePatched(int a1, const char *path, int a3, int a4, int a5) {
  int res = TAI_CONTINUE(int, scePafMiscLoadModuleRef, a1, path, a3, a4, a5);

  if (sceClibStrncmp(path, "vs0:vsh/shell/livespace_db.suprx", 32) == 0) {
    hooks[3] = taiHookFunctionImport(&sceLsdbGetTypeRef, "SceShell", 0x6BC25E17, 0xDEC358E4, sceLsdbGetTypePatched);
    hooks[4] = taiHookFunctionImport(&sceLsdbGetSelfPathRef, "SceShell", 0x6BC25E17, 0xD6B57313, sceLsdbGetSelfPathPatched);
  }

  return res;
}

static int sceSysmoduleLoadModuleInternalWithArgPatched(SceUInt32 id, SceSize args, void *argp, void *unk) {
  int res = TAI_CONTINUE(int, sceSysmoduleLoadModuleInternalWithArgRef, id, args, argp, unk);

  if (res >= 0 && id == 0x80000008) {
    hooks[2] = taiHookFunctionImport(&scePafMiscLoadModuleRef, "SceShell", 0x3D643CE8, 0xB3B5DF38, scePafMiscLoadModulePatched);
  }

  return res;
}

static int sceSysmoduleUnloadModuleInternalWithArgPatched(SceUInt32 id, SceSize args, void *argp, void *unk) {
  int res = TAI_CONTINUE(int, sceSysmoduleUnloadModuleInternalWithArgRef, id, args, argp, unk);

  if (res >= 0 && id == 0x80000008) {
    if (hooks[2] >= 0)
      taiHookRelease(hooks[2], scePafMiscLoadModuleRef);
  }

  return res;
}

void _start() __attribute__ ((weak, alias("module_start")));
int module_start(SceSize args, void *argp) {
  tai_module_info_t info;
  info.size = sizeof(info);
  if (taiGetModuleInfo("SceLsdb", &info) < 0) {
    hooks[0] = taiHookFunctionImport(&sceSysmoduleLoadModuleInternalWithArgRef, "SceShell", 0x03FCF19D, 0xC3C26339, sceSysmoduleLoadModuleInternalWithArgPatched);
    hooks[1] = taiHookFunctionImport(&sceSysmoduleUnloadModuleInternalWithArgRef, "SceShell", 0x03FCF19D, 0xA2F40C4C, sceSysmoduleUnloadModuleInternalWithArgPatched);
  } else {
    hooks[3] = taiHookFunctionImport(&sceLsdbGetTypeRef, "SceShell", 0x6BC25E17, 0xDEC358E4, sceLsdbGetTypePatched);
    hooks[4] = taiHookFunctionImport(&sceLsdbGetSelfPathRef, "SceShell", 0x6BC25E17, 0xD6B57313, sceLsdbGetSelfPathPatched);
  }

  return SCE_KERNEL_START_SUCCESS;
}

int module_stop(SceSize args, void *argp) {
  if (hooks[3] >= 0)
    taiHookRelease(hooks[3], sceLsdbGetTypeRef);
  if (hooks[4] >= 0)
    taiHookRelease(hooks[4], sceLsdbGetSelfPathRef);
  if (hooks[1] >= 0)
    taiHookRelease(hooks[1], sceSysmoduleUnloadModuleInternalWithArgRef);
  if (hooks[0] >= 0)
    taiHookRelease(hooks[0], sceSysmoduleLoadModuleInternalWithArgRef);

  return SCE_KERNEL_STOP_SUCCESS;
}
